<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 字库文件 -->
    <link rel="stylesheet" href="../assets/font/iconfont.css">
</head>

<body>
    <style>
        .notice-box {
            box-shadow: 0 0 8px #aaaaaa;
            background-color: white;
            border-radius: 8px;
            padding: 20px;
            width: 440px;
            display: flex;
            flex-flow: row nowrap;
            box-sizing: border-box;
            margin-right: 20px;
            position: fixed;
            right: 0px;
            opacity: 1;
            top: 0px;
            transition: top 300ms ease-in;
        }
        .notice-box.in{
            animation: moveIn 800ms ease 1 forwards;
        }
        .notice-box.out{
            animation: moveOut 400ms ease 1 forwards;
        }
        @keyframes moveIn {
            0%{
                right: -480px;
            }
            100%{
                right: 0px;
            }
        }
        @keyframes moveOut {
            0%{
                opacity: 1;
            }
            100%{
                opacity: 0;
            }
        }
        .notice-box .icon{
            padding-right: 14px;
        }
        .notice-box .icon .iconfont {
            font-size: 28px;
            line-height: 30px;
        }
        .notice-box .icon .icon-success1{
            color: #67c23a;
        }
        .notice-box .icon .icon-info{
            color: #909399;
        }
        .notice-box .icon .icon-warning{
            color: #e6a23c;
        }
        .notice-box .icon .icon-error{
            color: #f56c6c;
        }
        .notice-box .icon .icon-question{
            color: #409eff;
        }
        .context {
            line-height: 28px;
            flex-grow: 1;
            letter-spacing: 2px;
        }
        .context .title {
            font-size: 20px;
            font-weight: bold;
        }
        .context .title .close-btn {
            float: right;
            color: #999999;
            cursor: pointer;
        }
        .context .info {
            padding-top: 10px;
            color: #666666;
            font-size: 14px;
        }
    </style>
    <!-- 消息模板结构 -->
    <!-- 
        <div class="notice-box">
            <div class="icon">
                <i class="iconfont icon-error"></i>
            </div>
            <div class="context">
                <div class="title">
                    <span>错误</span>
                    <span class="close-btn">
                        <i class="iconfont icon-close2"></i>
                    </span>
                </div>
                <div class="info">测试消息</div>
            </div>
        </div> 
    -->
    <!-- <input type="button" value="notice" onclick="openNotice()"> -->
    <input type="button" value="success" onclick="openSuccess()">
    <input type="button" value="info" onclick="openInfo()">
    <input type="button" value="warning" onclick="openWarning()">
    <input type="button" value="error" onclick="openError()">
    <input type="button" value="question" onclick="openQuestion()">

    <script>
        // 采用ES6 class 技术封装的提示插件，采用全静态属性方法方式实现，使用时不用创建对象
        // 选择 class 技术方式可以简化 功能封装的复杂度
        // 2022年3月开始各公司调整技术研发方向，更推崇面向对象式开发，该案例和分页案例建议熟练掌握
        // 通过案例练习可有效锻炼综合性页面开发的思考思路，和逻辑熟练度
        class Notice {
            // 类型图标
            static icons = {
                success:"icon-success1",
                info:"icon-info",
                warning:"icon-warning",
                error:"icon-error",
                question:"icon-question"
            }
            // 生成元素的位置-会因页面元素数量动态调整
            static top = 20;
            // 页面元素存在的元素列表
            static noticeItems = {};
            // DOM元素创建方法
            static createElement (options={ title:"消息" }) {
                // 提示元素构建
                let noticeBox = document.createElement("div");
                // in 为元素进入动画
                noticeBox.className = "notice-box in";
                // 根据现有元素设置元素位置
                noticeBox.style.top = Notice.top + "px";

                // 判断是否需要生成图标
                if(options.icon){
                    let iconDom = document.createElement("div");
                    iconDom.classList.add("icon");
                    let iconFont = document.createElement("i");
                    // 根据传入参数，动态调整提示窗消息图标类型
                    iconFont.className = `iconfont ${Notice.icons[options.icon]}`;
                    iconDom.appendChild(iconFont);
                    noticeBox.appendChild(iconDom);
                }

                let noticeContext = document.createElement("div");
                noticeContext.classList.add("context");
                let noticeTitle = document.createElement("div");
                noticeTitle.classList.add("title");
                let noticeTitleInfo = document.createElement("span");
                // 限制标题长度，标题有效长度为  <=10
                noticeTitleInfo.textContent = options.title.slice(0,10);
                noticeTitle.appendChild( noticeTitleInfo );

                // 关闭按钮 - 后续会增加点击关闭事件
                let noticeTitleClose = document.createElement("span");
                noticeTitleClose.classList.add("close-btn")
                let noticeTitleCloseFont = document.createElement("i");
                noticeTitleCloseFont.className = "iconfont icon-close2";

                noticeTitleClose.appendChild(noticeTitleCloseFont);
                noticeTitle.appendChild(noticeTitleClose);
                noticeContext.appendChild(noticeTitle);
                
                // 判断是否有正文消息
                if(options.message){
                    let messageDom = document.createElement("div");
                    messageDom.classList.add("info");
                    messageDom.textContent = options.message;
                    noticeContext.appendChild(messageDom);
                }

                noticeBox.appendChild(noticeContext);

                // 记录下当前DOM方便后续操作
                // 随机唯一key生成，用于对应记录和操作，最好采用随机字符串，但随机字符串功能需要手动开发或引入额外库
                let key = Math.random(); 
                Notice.noticeItems[key] = noticeBox;
                window.document.body.appendChild( noticeBox )
                
                let domH = noticeBox.clientHeight+20;
                // 记录下次提示DOM出现的位置
                Notice.top += domH;
                // 定义 动画结束 事件，并判断在 moveOut 动画执行完成后删除对应DOM元素
                noticeBox.addEventListener("animationend",(event)=>{
                    if(event.animationName == "moveOut"){
                        window.document.body.removeChild(noticeBox)
                    }
                })
                // 构建消息到期后的后续操作
                let closeDomCallback = function(){
                    // out 为元素离开动画
                    noticeBox.className = "notice-box out";
                    // 记录下次元素的新位置
                    Notice.top -= domH;
                    // 记录当前元素位置用于后续判断，防止手动删除时，元素位置计算错误
                    let currentDomTop = parseFloat(noticeBox.style.top)
                    // 删除记录中的当前元素
                    delete Notice.noticeItems[key];
                    // 修正页面现存元素的新位置
                    for (let k in Notice.noticeItems) {
                        let tempTop = parseFloat(Notice.noticeItems[k].style.top);
                        // 位置小于当前元素高度时 不需要 进行位置更新
                        if(tempTop>currentDomTop){
                            tempTop -= domH;
                            Notice.noticeItems[k].style.top = tempTop +"px";
                        }
                    }
                }
                // 计时器关闭方法
                let timeer = setTimeout(closeDomCallback, 5000);
                // 元素手动事件关闭方法
                noticeTitleClose.addEventListener("click",()=>{
                    // 清除当前提示元素的计时器，防止多次操作
                    clearTimeout(timeer);
                    closeDomCallback();
                })
            }
            // 统一提示方法
            static notice(options={}){
                let opt = Object.assign({
                    title:"消息"
                },options)
                Notice.createElement( opt );
            }
            // 成功提示方法
            static success(options={}){
                let opt = Object.assign({
                    icon:"success",
                    title:"成功"
                },options)
                Notice.createElement( opt );
            }
            // 消息提示方法
            static info(options={}){
                let opt = Object.assign({
                    icon:"info",
                    title:"消息"
                },options)
                Notice.createElement( opt );
            }
            // 警告提示方法
            static warning(options={}){
                let opt = Object.assign({
                    icon:"warning",
                    title:"警告"
                },options)
                Notice.createElement( opt );
            }
            // 失败提示方法
            static error(options={}){
                let opt = Object.assign({
                    icon:"error",
                    title:"错误"
                },options)
                Notice.createElement( opt );
            }
            // 问题提示方法
            static question(options={}){
                let opt = Object.assign({
                    icon:"question",
                    title:"问题"
                },options)
                Notice.createElement( opt );
            }
        }

        // 页面事件
        function openNotice(){
            Notice.notice({
                title:"测试无图标提示",
                message:"错误提示文本内容"
            });
        }
        function openSuccess(){
            Notice.success();
        }
        function openInfo(){
            Notice.info({
                // title:"消息标题"
            });
        }
        function openWarning(){
            Notice.warning({
                // message:"警告提示文本内容警告提示文本内容警告提示文本内容警告提示文本内容警告提示文本内容警告提示文本内容警告提示文本内容"
            });
        }
        function openError(){
            Notice.error({
                // title:"错误标题",
                // message:"错误提示文本内容"
            });
        }
        function openQuestion(){
            Notice.question();
        }
    </script>
</body>

</html>